home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / eText5 / Source / Bounce.bproj / LinesView.m < prev    next >
Encoding:
Text File  |  1994-05-16  |  6.9 KB  |  231 lines

  1. /*
  2.  * LinesView.m, a small sample view for showing timed entries & userpaths.
  3.  * Author: Ali T. Ozer, NeXT Computer, Inc.
  4.  * Written March 19, 1989.
  5.  * Updated for 2.0 Oct 16, 1990 by Jayson Adams to use UserPath.[hm].
  6.  * Updated for 3.0 March 24, 1992 by Ali Ozer
  7.  *
  8.  * You may freely copy, distribute and reuse the code in this example.
  9.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  10.  * fitness for any particular use.
  11.  *
  12.  * LinesView draws a number of connected lines whose endpoints bounce around
  13.  * randomly within the bounds of the view. The endpoints are stored in
  14.  * an data array which is passed to PostScript as a user path. The
  15.  * animation is performed by calling the "animate" method as often as
  16.  * possible through a timed entry.
  17.  */
  18.  
  19. #import <appkit/appkit.h>
  20. #import <libc.h>                // For random(), etc...
  21. #import <dpsclient/wraps.h>    // For PS and DPS function prototypes
  22. #import "LinesView.h"
  23.  
  24. #define RANDINT(n) (random() % (n+1))        // Return random integer 0..n
  25.  
  26. #define XVEL corners[count].xVel  // Some slimy shortcuts, asuuming we're
  27. #define YVEL corners[count].yVel  // using "count" as corner counter.
  28. #define XLOC corners[count].xLoc
  29. #define YLOC corners[count].yLoc
  30.  
  31. #define MAXVEL 12        // Maximum velocity of corners
  32.  
  33.  
  34. @implementation LinesView
  35.  
  36. - initFrame:(const NXRect *)rect
  37. {
  38.     [super initFrame:rect];
  39.  
  40.   /* create a user path */
  41.     userPath = newUserPath();
  42.     
  43.     running = NO;
  44.  
  45.     return self;
  46. }
  47.  
  48. - free
  49. {
  50.   /* be sure to stop the timed entry */
  51.     if (running) {
  52.     DPSRemoveTimedEntry(linesTimedEntry);
  53.     }
  54.     freeUserPath(userPath);
  55.     
  56.     return [super free];
  57. }
  58.  
  59. void DrawIt(DPSTimedEntry te, double timeNow, void *data)
  60. {
  61.   /* we set data to self so we can call this method from the timed entry */
  62.     [(id)data animate];
  63. }
  64.  
  65. - toggleRun:sender
  66. {
  67.   /* start or stop the timed entry (we're called by a two-state button) */
  68.     if (running) {
  69.     DPSRemoveTimedEntry(linesTimedEntry);
  70.     running = NO;
  71.     } else {
  72.       /* Call the DrawIt() function as often as possible... */
  73.     linesTimedEntry = DPSAddTimedEntry(0.01, &DrawIt, self,
  74.                        NX_BASETHRESHOLD);
  75.     running = YES;
  76.     }
  77.  
  78.     return self;
  79. }
  80.  
  81. - off
  82. {
  83.     if (running) {
  84.         DPSRemoveTimedEntry(linesTimedEntry);
  85.         running = NO;
  86.     }
  87.     return self;
  88. }
  89. /*
  90.  * This method should be connected to a UI object capable of generating
  91.  * int numbers. Note that to successfully detect the initial value of this
  92.  * slider as set through IB, we also declare an outlet named "numberOfCorners,"
  93.  * and connect it to this UI object. Thus this method (setNumberOfCorners:)
  94.  * gets called when the .nib is being loaded, and we can detect the
  95.  * initial value of the slider.  Because at initialization time the window
  96.  * isn't up yet, we won't really update the display at that time, even though
  97.  * display is called below.
  98.  */
  99. - setNumberOfCorners:sender
  100. {
  101.     int       count;
  102.     int    oldNumCorners = numCorners;
  103.  
  104.   /* set the number of corners based on the "corners" slider */
  105.     numCorners = MIN(MAXNUMCORNERS, MAX([sender intValue], MINNUMCORNERS));
  106.  
  107.   /* set the new corner starting positions & velocities */
  108.     for (count = oldNumCorners; count < numCorners; count++) {
  109.       XLOC = (int)(bounds.size.width) / 2;      
  110.       YLOC = (int)(bounds.size.height) / 2;      
  111.       XVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
  112.       YVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
  113.     }
  114.     [self display];
  115.  
  116.     return self;
  117. }
  118.  
  119. - drawSelf:(const NXRect *)rects :(int)rectCount
  120. {
  121.     NXRect r2;
  122.     int      count;
  123.     
  124.   /* fill with the background color */
  125.     [[self superview] getBounds:&r2];
  126.     NXDrawWhiteBezel(&bounds, &r2);
  127.   //  NXEraseRect(&bounds);
  128.     
  129.     PSsetgray(NX_BLACK);
  130.     PSsetlinewidth(0.0);
  131.  
  132.   /* "plot" the points */
  133.     beginUserPath(userPath, NO);
  134.     for (count = 0; count < numCorners; count++) {
  135.     if (count) {
  136.         UPlineto(userPath, XLOC, YLOC);
  137.     } else {
  138.         UPmoveto(userPath, XLOC, YLOC);
  139.     }
  140.     }
  141.     closePath(userPath);
  142.     
  143.   /* draw it */
  144.     endUserPath(userPath, dps_ustroke);
  145.     sendUserPath(userPath);
  146.     
  147.     return self;
  148. }
  149.  
  150. - animate
  151. /*
  152.  * Lines is an unusual animation program in that it runs untimed; that is,
  153.  * it runs as fast as the CPU will allow, and it doesn't care that on faster
  154.  * CPUs the animation will run faster. An animation or game application
  155.  * will usually want to limit to frame rate to a value (for instance, 30
  156.  * frames a second), and on hardware not capable of that rate, end up doing
  157.  * the best it can. Such an application would also look at the time
  158.  * that actually passed between frames and increment the animation or game play
  159.  * accordingly. (See the sources to the BreakApp example on how it does it
  160.  * animation timing. BreakApp also allocates a graphic state for its view so
  161.  * that the lock/unlockFocus is faster.)
  162.  *
  163.  * Lines accomplishes its goal of running as fast as possible by creating
  164.  * a timed entry with a 0.0 second period. This means that the timed entry
  165.  * will fire and this method (animate) will be called as soon as possible.
  166.  * To make things even faster, we stay in this method until some event comes
  167.  * along. Staying in this method has the advantage that we do not need to
  168.  * lock or unlockFocus every frame. Of course, this only works as desired
  169.  * if the timed entry was placed with a period of 0.0 seconds.
  170.  * 
  171.  * If an event comes along, we return from this method and process the event.
  172.  * Then, unless the user stopped the animation, the timed entry brings us
  173.  * right back in to continue with the animation.
  174.  *
  175.  * Lines uses a buffered output window as a means to fake double-buffered
  176.  * animation. The current frame is drawn directly into the window. However,
  177.  * because the window is buffered, the drawing goes to the backing store,
  178.  * and not the screen.  Only when the frame is complete does Lines flush the
  179.  * window contents to the screen; this process is fast and provides a
  180.  * flicker-free update.  The next frame is then drawn into the backing store,
  181.  * and the cycle continues.
  182.  */
  183. {
  184.     int count;  
  185.     NXEvent dummyEvent;  // For peeking at the event queue. 
  186.     
  187.      if (!running) return nil;
  188.     [self lockFocus];
  189.     
  190.     do {
  191.       /* move all the corners... */
  192.     for (count = 0; count < numCorners; count++) {
  193.         XLOC += XVEL;
  194.         YLOC += YVEL;
  195.  
  196.       /*
  197.        * Detect collision with sides; if we collide, bounce back in some
  198.        * random fashion.
  199.        */
  200.         if (XLOC >= bounds.size.width) {
  201.         XLOC = bounds.size.width-1;
  202.         XVEL = -1-RANDINT(MAXVEL);
  203.         } else if (XLOC < bounds.origin.x) {
  204.         XLOC = bounds.origin.x;
  205.         XVEL = 1+RANDINT(MAXVEL);
  206.         }
  207.         if (YLOC >= bounds.size.height) {
  208.         YLOC = bounds.size.height-1;
  209.         YVEL = -1-RANDINT(MAXVEL);
  210.         } else if (YLOC < bounds.origin.y) {
  211.         YLOC = bounds.origin.y;
  212.         YVEL = 1+RANDINT(MAXVEL);
  213.         }
  214.     }
  215.  
  216.       /* draw our path and flush to the screen */
  217.     [self drawSelf:&bounds :1];
  218.     [window flushWindow];
  219.     
  220.     } while ([NXApp peekNextEvent:NX_ALLEVENTS
  221.             into:&dummyEvent 
  222.             waitFor:0.0
  223.             threshold:NX_BASETHRESHOLD] == NULL);
  224.  
  225.     [self unlockFocus];
  226.  
  227.     return self;
  228. }
  229.  
  230. @end
  231.